const jimp = require('jimp')
const fs = require('fs')
const config = require(process.cwd() + '/src/config/config')
const fileUtils = require(process.cwd() + '/src/utils/file')
const commonUtils = require(process.cwd() + '/src/utils/common')
const autotilesOptions = require(process.cwd() + '/src/config/autotilesOptions')

const tiles = {
  // @section 以下方法均为autotile方法
  
  // 添加一个AutoTile
  addAutotile: async (imagePath) => {
    let image = await jimp.read(imagePath)
    const size = tiles.getImageSize(image)
    if (size.height !== 4 * autotilesOptions.memberSize ||
      size.width % (autotilesOptions.memberSize * 3) !== 0) {
      return Promise.reject('图片尺寸不符合标准')
    }
    
    // 帧数统计，切割出每一帧的大图
    let frames = tiles.cutAutotileFrames(image)
    
    // 对于每一帧的大图，分割成48个素材
    let pieces = tiles.cutAutotilePieces(frames)
    
    // 获取后缀
    let arr = imagePath.split('.')
    const tail = arr[arr.length - 1]
    // 获取保存路径
    arr.splice(arr.length - 1, 1)
    const memberPath = arr.join('.')
    // 素材按照指定的规则拼接成每一个单元
    await tiles.getAutotileMembers(pieces, memberPath, tail)
  },
  
  // 获取Autotile展示用的小图资源地址
  getSmallAutotileSrc: async (name) => {
    // 获取文件列表
    const files = await fs.promises.readdir(config.autotilesPath + name)
    // 随便选取一个文件，获取后缀
    const tail = fileUtils.getFileTail(files[0])
    
    // 获取最小的展示图片的路径
    // 后缀为s,一定要与autotilesOptions同步
    const noFrameNum = autotilesOptions.rules.length + 1
    let smallPath = config.autotilesSrcPath + name + '/' + name + '_' + autotilesOptions.showSmallTail
    // 如果有多帧,再加上帧数前缀
    if (files.length > noFrameNum) {
      smallPath = smallPath + '_0' 
    }
    smallPath = smallPath + '.' + tail
    return smallPath
  },
  
  // 获取图片的尺寸
  getImageSize (image) {
    return {
      width: image.bitmap.width,
      height: image.bitmap.height
    }
  },
  
  // 创建一个空白图
  createEmptyImage (width, height) {
    return new Promise((resolve, reject) => {
      new jimp(width, height, (err, image) => {
        if (err) {
          return reject(err)
        }
        resolve(image)
      })
    })
  },
  
  // 将AutoTiles图片按照帧数进行切割
  cutAutotileFrames (image) {
    const size = tiles.getImageSize(image)
    const frameNum = Math.floor(size.width / (autotilesOptions.memberSize * 3))
    const height = autotilesOptions.memberSize * 4
    const width = autotilesOptions.memberSize * 3
    if (frameNum > 1) {
      let frames = []
      for (let i = 0; i < frameNum; i++) {
        let cloneImage = image.clone()
        let frame = cloneImage.crop(width * i, 0, width, height)
        frames.push(frame)
      }
      return frames
    }
    // 只有一帧，直接返回
    return [image]
  },
  
  // 切割成16*16的素材
  cutAutotilePieces (frames) {
    const pieces = []
    const len = frames.length
    for (let i = 0; i < len; i++) {
      const frame = frames[i]
      const size = tiles.getImageSize(frame)
      const rowNum = Math.floor(size.height / autotilesOptions.pieceSize)
      const colNum = Math.floor(size.width / autotilesOptions.pieceSize)
      const framePieces = []
      for (let j = 0; j < rowNum; j++) {
        for (let k = 0; k < colNum; k++) {
          let cloneFrame = frame.clone()
          let piece = cloneFrame.crop(k * autotilesOptions.pieceSize, j * autotilesOptions.pieceSize,
            autotilesOptions.pieceSize, autotilesOptions.pieceSize)
          framePieces.push(piece)
        }
      }
      pieces.push(framePieces)
    }
    return pieces
  },
  
  // 填充并保存单张Autotile图块
  saveAutotileMember: async (framePieces, rule, imagePath) => {
    let image = await tiles.createEmptyImage(autotilesOptions.memberSize, autotilesOptions.memberSize)
    // 根据rules填充四个角
    image.composite(framePieces[rule.pieces[0]], 0, 0)
    image.composite(framePieces[rule.pieces[1]], autotilesOptions.pieceSize, 0)
    image.composite(framePieces[rule.pieces[2]], 0, autotilesOptions.pieceSize)
    image.composite(framePieces[rule.pieces[3]], autotilesOptions.pieceSize, autotilesOptions.pieceSize)
    // 保存
    await image.writeAsync(imagePath)
  },
  
  // 组合成图片并压入列表
  getAutotileMembers: async (pieces, memberPath, tail) => {
    const members = []
    const rules = autotilesOptions.rules
    const len = pieces.length
    const rulesLen = rules.length
    for (let i = 0; i < len; i++) {
      const framePieces = pieces[i]
      for (let j = 0; j < rulesLen; j++) {
        const rule = rules[j]
        
        // 生成保存路径
        let imagePath = memberPath + '_' + rule.name
        // 如果有多帧，再加上数字后缀
        if (len > 1) {
          imagePath = imagePath + '_' + i
        }
        imagePath = imagePath + '.' + tail
        let member = tiles.saveAutotileMember(framePieces, rule, imagePath)
        
        members.push(member)
      }
    }
    
    await Promise.all(members)
  },
  
  // @section 以下均为tileset方法
  /*
    @prop imagePath 大图片的原始地址
    @prop category 图片资源所在分类
    @prop tileset 图集的名称
    @prop width height 图集单元的宽高
  */
  addTilesetByImage: async (imagePath, tileset, width, height, colNum) => {
    let image = await jimp.read(imagePath)
    const size = tiles.getImageSize(image)
    if (size.width < width || size.height < height) {
      return Promise.reject('图片尺寸不符合标准')
    }
    
    // 拼接出图集资源保存地址
    const resourcePath = config.imagesPath + tileset + '/'
    // 检验并创建保存的文件夹
    await fileUtils.createFolder(resourcePath)
    
    // 获取文件后缀
    const tail = fileUtils.getFileTail(imagePath)
    // 获取资源地址
    const tilesPath = resourcePath + tileset
    // 切割大图，并保存到目标分类，返回保存地址list
    let tilesPathList = await tiles.cutTile(image, tileset, tail, width, height, colNum)
    return tilesPathList
  },
  
  // 保存单张图块
  saveTile: async (image, tileItemPath, row, col, width, height) => {
    let cloneImage = image.clone()
    let tileItem = cloneImage.crop(width * col, height * row, width, height)
    await tileItem.writeAsync(tileItemPath)
  },
  
  // 保存空白填充图块
  saveEmptyTile: async (tileItemPath, width, height) => {
    let image = await tiles.createEmptyImage(width, height)
    await image.writeAsync(tileItemPath)
  },
  
  // 切割瓦片，并进行保存
  cutTile: async (image, tileset, tail, width, height, colNum) => {
    const tilesList = []
    const srcList = []
    const size = tiles.getImageSize(image)
    const rows = Math.floor(size.height / height)
    const cols = Math.floor(size.width / width)
    // 有效图块总数
    const total = rows * cols
    // 为了凑满一行，填充空图片总数
    let left = colNum - (total % colNum)
    if (left === colNum) {
      left = 0
    }
    
    for (let i = 0; i < rows; i++) {
      for (let j = 0; j < cols; j++) {
        // id为当前时间戳 + 8位随机串
        const id = new Date().getTime() + commonUtils.randomString(false, 8)
        const tileItemPath = config.imagesPath + tileset + '/' + 'tile_' + id + '.' + tail
        // src只需要图片名即可，中间路径根据setting拼接
        const tileItemSrc = fileUtils.getLastName(tileItemPath)
        let tile = tiles.saveTile(image, tileItemPath, i, j, width, height)
        srcList.push({
          id: id,
          src: tileItemSrc
        })
        tilesList.push(tile)
      }
    }
    
    // 填充空图片
    for (let i = 0; i < left; i++) {
      // id为当前时间戳 + 8位随机串
      const id = new Date().getTime() + commonUtils.randomString(false, 8)
      const tileItemPath = config.imagesPath + tileset + '/' + 'tile_' + id + '.' + tail
      // src只需要图片名即可，中间路径根据setting拼接
      const tileItemSrc = fileUtils.getLastName(tileItemPath)
      let tile = tiles.saveEmptyTile(tileItemPath, width, height)
      srcList.push({
        id: id,
        src: tileItemSrc
      })
      tilesList.push(tile)
    }
    
    await Promise.all(tilesList)
    return srcList
  },
  
  // 将图集setting里的src拼接成输出到前端的src
  tileImageSrc (list, tileset) {
    list = list.map((item) => {
      return {
        id: item.id,
        src: config.imagesSrcPath + tileset + '/' + item.src
      }
    })
    return list
  },
  
  // 获取所有的图集图块信息,返回列表
  getAllTiles: async () => {
    // 先获取所有图集文件
    const tilesetsList = await fs.promises.readdir(config.tilesetsPath)
    
    // 再读取所有图集数据
    let progressList = []
    let len = tilesetsList.length
    for (let i = 0; i < len; i++) {
      let progress = fs.promises.readFile(config.tilesetsPath + tilesetsList[i])
      progressList.push(progress)
    }
    let tilesDataArr = await Promise.all(progressList)
    
    // 取所有图集数据中有用的部分
    len = tilesDataArr.length
    let tilesData = []
    for (let i = 0; i < len; i++) {
      tilesDataArr[i] = JSON.parse(tilesDataArr[i])
      tilesDataArr[i].list = tilesDataArr[i].list.map((tile) => {
        return {
          id: tile.id,
          src: config.imagesSrcPath + tilesDataArr[i].name + '/' + tile.src
        }
      })
      tilesData = tilesData.concat(tilesDataArr[i].list)
    }
    
    return tilesData
  },
  
  // 将多个对象中的src字段有ID形式转换为前端可以接受的资源路径的形式
  // 传入一个对象,将对该对象的所有字段进行深度递归解析
  // 只解析src以及srcList字段
  analyseSrc: async (object, tilesData = null) => {
    const SRC = 'src'
    const SRC_LIST = 'srcList'
    
    // 入参不是对象,也不是数组
    if (!commonUtils.type.isObject(object) && !Array.isArray(object)) {
      return object
    }
    
    let keys = Object.keys(object)
    
    // tilesData中为所有图集资源,必须进行一次获取
    if (!tilesData) {
      // 先获取所有图集文件
      tilesData = await tiles.getAllTiles()
    }
    
    if (keys.indexOf(SRC) >= 0) {
      // 处理资源字符串
      const index = tilesData.findIndex(tile => tile.id === object[SRC])
      if (index >= 0 && object[SRC].indexOf('/') < 0) {
        // 匹配成功,且SRC字段原本就不是一个路径时,进行替换
        object[SRC] = tilesData[index].src
        // console.log(object)
      }
    }
    
    if (keys.indexOf(SRC_LIST) >= 0 && Array.isArray(object[SRC_LIST])) {
      // 处理资源列表
      const len = object[SRC_LIST].length
      for (let i = 0; i < len; i++) {
        const index = tilesData.findIndex(tile => tile.id === object[SRC_LIST][i])
        if (index >= 0 && object[SRC_LIST][i].indexOf('/') < 0) {
          // 匹配成功,且SRC字段原本就不是一个路径时,进行替换
          object[SRC_LIST][i] = tilesData[index].src
        }
      }
    }
    
    // 递归解析所有属性
    let keyProgress = keys.map((key) => {
      return tiles.analyseSrc(object[key], tilesData)
    })
    await Promise.all(keyProgress)
    return object
  },
  
  // 将多个对象中的src字段由前端可以接受的资源路径的形式转换为ID形式
  // 传入一个对象,将对该对象的所有字段进行深度递归解析
  // 只解析src以及srcList字段
  zipSrc (object) {
    const SRC = 'src'
    const SRC_LIST = 'srcList'
    
    // 路径匹配串
    const SRC_MACTH = '/tile_'
    const MATCH_LEN = SRC_MACTH.length
    
    // 入参不是对象,也不是数组
    if (!commonUtils.type.isObject(object) && !Array.isArray(object)) {
      return object
    }
    
    let keys = Object.keys(object)
    
    
    if (keys.indexOf(SRC) >= 0) {
      // 处理资源字符串
      const tileKeyIndex = object[SRC].indexOf(SRC_MACTH)
      if (tileKeyIndex >= 0) {
        object[SRC] = object[SRC].slice(tileKeyIndex + MATCH_LEN)
        object[SRC] = fileUtils.cutFileTail(object[SRC])
      }
    }
    
    if (keys.indexOf(SRC_LIST) >= 0 && Array.isArray(object[SRC_LIST])) {
      // 处理资源列表
      const len = object[SRC_LIST].length
      for (let i = 0; i < len; i++) {
        // 处理资源字符串
        const tileKeyIndex = object[SRC_LIST][i].indexOf(SRC_MACTH)
        if (tileKeyIndex >= 0) {
          object[SRC_LIST][i] = object[SRC_LIST][i].slice(tileKeyIndex + MATCH_LEN)
          object[SRC_LIST][i] = fileUtils.cutFileTail(object[SRC_LIST][i])
        }
      }
    }
    
    const len = keys.length
    for (let i = 0; i < len; i++) {
      object[keys[i]] = tiles.zipSrc(object[keys[i]])
    }
    
    return object
  },
}

module.exports = tiles